# ICEMGenerator - 高效的ICEM CFD网格划分Replay Script生成工具
# 模块： Part类，用于将指定的对象创建成Part，并赋予边界条件
# 作者： weixc
# 邮箱： 12028045 [at] zju.edu.cn
# 项目主页： https://gitee.com/weixinchong/icemgenerator

from collections.abc import Iterable
from .curve import Curve
from .surface import Surface
from .param_check import BoundaryConditionError

bc_names = {
    'AXIS': 'axis',
    'FAN': 'fan, porous-jump, radiator',
    'FLUID': 'fluid',
    'IFACE': 'interface',
    'INTER': 'interior',
    'MASFI': 'mass-flow-inlet',
    'OUTF': 'outflow',
    'PER': 'periodic',
    'PRESF': 'pressure-far-field',
    'PRESI': 'pressure-inlet, inlet-vent, intake-fan',
    'PRESO': 'pressure-outlet, exhaust-fan, outlet-vent',
    'SOLID': 'solid',
    'SYM': 'symmetry',
    'VELI': 'velocity-inlet',
    'WALL': 'wall'
}


class PartMeshParams():

    law_undefined = -1
    law_exponental = 0
    law_linear = 1
    law_wb_exponental = 3

    def __init__(self, prism_mesh: bool = False, max_size: float = 0, height: float = 0, height_ratio: float = 0,
                 num_layers: int = 0, tetra_size_ratio: float = 0, tetra_width: float = 0, min_size_limit: float = 0, 
                 max_deviation: float = 0, prism_height_limit_factor: float = 0, prism_growth_law: str = law_undefined,
                 internal_wall: bool = False, split_wall: bool = False):
        '''网格局部细化参数设置，与Part Mesh Setup中的对应参数一致，参数实际含义请参考官方文档'''
        self.prism = int(prism_mesh)
        self.emax = max_size
        self.ehgt = height
        self.hrat = height_ratio
        self.nlay = num_layers
        self.erat = tetra_size_ratio
        self.ewid = tetra_width
        self.emin = min_size_limit
        self.edev = max_deviation
        self.prism_height_limit = prism_height_limit_factor
        self.law = prism_growth_law
        self.internal_wall = int(internal_wall)
        self.split_wall = int(split_wall)


class Part():
    '''创建一个Part'''

    members = []  #现有Part的成员列表
    parted_objects = []  #已经被加入Part的对象列表

    def members_create_replay() -> str:
        '''生成创建所有Part的Replay Script
        :return: 生成的Replay Script'''
        text = ''
        for p in Part.members:
            text += p.replay_create() + '\n'
        text += 'ic_delete_empty_parts\n'
        return text

    def members_apply_bc_replay(fName: str) -> str:
        '''生成为所有Part定义边界条件的Replay Script
        :param fName: 输出边界条件的文件名（不带扩展名）
        :return: 生成的Replay Script'''
        text = 'ic_boco_solver {ANSYS Fluent}\n' \
             + 'ic_solution_set_solver {ANSYS Fluent} 1\n' \
             + 'ic_solver_mesh_info {ANSYS Fluent}\n'
        for p in Part.members:  #给定Part的边界条件
            if p.bc is not None:
                text += p.replay_apply_bc() + '\n'
        text = text + 'ic_boco_save %s.fbc\n' % fName \
             + 'ic_boco_save_atr %s.atr\n' % fName \
             + 'ic_delete_empty_parts\n'
        return text

    def wallFromRemainCurves():
        '''将未加入到任何Part的线定义为壁面，适用于二维模型
        :return: 定义的壁面Part'''
        curves = []
        for c in Curve.members:
            if c not in Part.parted_objects:
                curves.append(c)
        return Part(curves, 'WALL', 'WALL')

    def wallFromRemainSurfaces():
        '''将未加入到任何Part的面定义为壁面，适用于三维模型
        :return: 定义的壁面Part'''
        surfaces = []
        for s in Surface.members:
            if s not in Part.parted_objects:
                surfaces.append(s)
        return Part(surfaces, 'WALL', 'WALL')

    def __init__(self, objects: Iterable, bc: str, name: str, part_mesh_params: PartMeshParams = None):
        '''创建由一系列对象（线、面或体）构成的Part
        :param objects: 要归入当前Part的对象列表，可以是Point、Curve、Surface或Body
        :param bc: 边界条件，可选值包括:
            (1)对于体: FLUID, SOLID
            (2)对于面: AXIS, FAN(fan, porous-jump, radiator), FLUID, IFACE(interface), INTER(interior), MASFI(mass-flow-inlet), OUTF(outflow), PER(periodic), PRESF(pressure-far-field), PRESI(pressure-inlet, inlet-vent, intake-fan), PRESO(pressure-outlet, exhaust-fan, outlet-vent), SOLID, SYM(symmetry), VELI(velocity-inlet), WALL
            (3)对于线: AXIS, FAN(fan, porous-jump, radiator), IFACE(interface), INTER(interior), MASFI(mass-flow-inlet), OUTF(outflow), PER(periodic), PRESF(pressure-far-field), PRESI(pressure-inlet, inlet-vent, intake-fan), PRESO(pressure-outlet, exhaust-fan, outlet-vent), SYM(symmetry), VELI(velocity-inlet), WALL
            (4)None：只创建Part，不指定边界条件
        :param name: Part的名称
        :param part_mesh_params: 局部网格细化参数，未给出时，不进行局部网格细化'''
        if bc is not None and bc not in bc_names.keys():
            raise BoundaryConditionError('在Part`%s`中，边界条件`%s`不存在' % (name, bc))
        self.objects = objects
        self.bc = bc
        self.name = name
        Part.members.append(self)
        Part.parted_objects += objects
        self.part_mesh_params = part_mesh_params

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()

    def setName(self, name: str):
        '''设置Part的名称
        :param name: 要设定的Part名称'''
        self.name = name

    def replay_create(self) -> str:
        '''生成创建Part的Replay Script
        :return: 生成的Replay Script'''
        obj_pnt_expr = ''
        obj_crv_expr = ''
        obj_srf_expr = ''
        obj_body_expr = ''
        for oi in self.objects:
            obj_type = str(type(oi))
            if 'Point' in obj_type:
                obj_pnt_expr += '%s ' % oi
            elif 'Line' in obj_type or 'Ring' in obj_type or 'Arc' in obj_type:
                obj_crv_expr += '%s ' % oi
            elif 'Surface' in obj_type:
                obj_srf_expr += '%s ' % oi
            elif 'Body' in obj_type:
                obj_body_expr += '%s.0 ' % oi
        text = ''
        if obj_pnt_expr != '':
            text += 'ic_geo_set_part point {%s} %s 0\n' % (obj_pnt_expr[:-1], self.name)
        if obj_crv_expr != '':
            text += 'ic_geo_set_part curve {%s} %s 0\n' % (obj_crv_expr[:-1], self.name)
        if obj_srf_expr != '':
            text += 'ic_geo_set_part surface {%s} %s 0\n' % (obj_srf_expr[:-1], self.name)
        if obj_body_expr != '':
            text += 'ic_geo_set_part body {%s} %s 0\n' % (obj_body_expr[:-1], self.name)
        return text[:-1]

    def replay_apply_bc(self) -> str:
        '''生成为Part定义边界条件的Replay Script'''
        return 'ic_boco_set %s {{1 %s 0}}' % (self, self.bc)

    def info(self) -> str:
        '''获得Part的信息
        :return: 输出的信息'''
        text = 'Part %s' % self
        obj_expr = ' '.join([str(oi) for oi  in self.objects])
        text += '\nObjects Included: %s' % obj_expr
        text += '\nBoundary Condition: %s' % bc_names[self.bc]
        return text